package com.smart.community.user.service.impl;

import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.UUID;

import javax.imageio.ImageIO;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.smart.community.commons.exception.BusinessException;
import com.smart.community.commons.result.ResultCode;
import com.smart.community.user.entity.UserUploadFile;
import com.smart.community.user.mapper.UserUploadFileMapper;
import com.smart.community.user.service.UserUploadFileService;

import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;

/**
 * 用户上传文件服务实现类
 * 
 * @author Wu.Liang
 * @since 2025-01-30
 * @version 1.0.0
 */
@Slf4j
@Service
public class UserUploadFileServiceImpl extends ServiceImpl<UserUploadFileMapper, UserUploadFile> implements UserUploadFileService {
    @Autowired
    private MinioClient minioClient;
    @Value("${minio.bucket-name:smart-community}")
    private String bucketName;
    @Value("${minio.endpoint:http://localhost:9000}")
    private String endpoint;
    @Value("${user.upload.max-file-size:10485760}")
    private Long maxFileSize;
    @Value("${user.upload.allowed-types:jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx}")
    private String allowedTypes;


    @Override
    public UserUploadFile uploadFile(MultipartFile file, Long userId, String uploadType, String uploadSource) throws Exception {
        // 检查MinIO客户端是否可用
        if (minioClient == null) {
            log.error("MinIO客户端未初始化，文件上传功能不可用");
            throw new BusinessException(ResultCode.FILE_UPLOAD_ERROR, "文件上传服务不可用，请联系管理员");
        }

        // 验证文件
        validateFileSize(file, maxFileSize);
        validateFileType(file, allowedTypes.split(","));

        // 生成文件信息
        String originalFilename = file.getOriginalFilename();
        String fileExtension = getFileExtension(originalFilename);
        String fileType = file.getContentType();
        Long fileSize = file.getSize();

        // 计算文件哈希值
        String md5Hash = DigestUtils.md5DigestAsHex(file.getBytes());
        String sha256Hash = calculateSHA256(file.getBytes());

        // 检查文件是否已存在（通过MD5）
        UserUploadFile existingFile = baseMapper.selectByMd5Hash(md5Hash);
        if (existingFile != null) {
            log.warn("文件已存在，MD5: {}", md5Hash);
            return existingFile;
        }

        // 生成存储路径
        String objectName = generateObjectName(uploadType, fileExtension);
        String filePath = "/" + objectName;
        // 直接使用MinIO地址生成文件访问URL
        String fileUrl = endpoint + "/" + bucketName + filePath;

        // 上传到MinIO
        try (InputStream inputStream = file.getInputStream()) {
            minioClient.putObject(
                PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(inputStream, fileSize, -1)
                    .contentType(fileType)
                    .build()
            );
            log.info("文件上传到MinIO成功: bucket={}, object={}, size={}", bucketName, objectName, fileSize);
        } catch (Exception e) {
            log.error("文件上传到MinIO失败: bucket={}, object={}, error={}", bucketName, objectName, e.getMessage(), e);
            throw new BusinessException(ResultCode.FILE_UPLOAD_ERROR, "文件上传失败: " + e.getMessage());
        }

        // 创建文件记录
        UserUploadFile uploadFile = new UserUploadFile();
        uploadFile.setUserId(userId);
        uploadFile.setFileName(originalFilename);
        uploadFile.setFilePath(filePath);
        uploadFile.setFileUrl(fileUrl);
        uploadFile.setFileSize(fileSize);
        uploadFile.setFileType(fileType);
        uploadFile.setFileExtension(fileExtension);
        uploadFile.setBucketName(bucketName);
        uploadFile.setObjectName(objectName);
        uploadFile.setUploadType(uploadType);
        uploadFile.setUploadSource(uploadSource);
        uploadFile.setMd5Hash(md5Hash);
        uploadFile.setSha256Hash(sha256Hash);
        uploadFile.setStatus(1); // 正常状态
        uploadFile.setDownloadCount(0);
        uploadFile.setViewCount(0);
        uploadFile.setCreateBy(userId);
        uploadFile.setUpdateBy(userId);

        // 如果是图片，获取图片尺寸
        if (isImage(fileType)) {
            uploadFile.setIsImage(1);
            try (InputStream inputStream = file.getInputStream()) {
                BufferedImage image = ImageIO.read(inputStream);
                if (image != null) {
                    uploadFile.setImageWidth(image.getWidth());
                    uploadFile.setImageHeight(image.getHeight());
                }
            }
        } else {
            uploadFile.setIsImage(0);
        }

        // 保存到数据库
        save(uploadFile);

        log.info("文件上传成功，文件ID: {}, 用户ID: {}, 文件名: {}", uploadFile.getId(), userId, originalFilename);
        return uploadFile;
    }

    @Override
    public List<UserUploadFile> getFilesByUserId(Long userId) throws Exception {
        return baseMapper.selectByUserId(userId);
    }

    @Override
    public List<UserUploadFile> getFilesByUserIdAndType(Long userId, String uploadType) throws Exception {
        return baseMapper.selectByUserIdAndType(userId, uploadType);
    }

    @Override
    public boolean deleteFile(Long fileId, Long userId) throws Exception {
        UserUploadFile file = getById(fileId);
        if (file == null) {
            throw new BusinessException(ResultCode.DATA_NOT_FOUND, "文件不存在");
        }

        // 检查权限（只能删除自己的文件）
        if (!file.getUserId().equals(userId)) {
            throw new BusinessException(ResultCode.PERMISSION_DENIED, "无权限删除此文件");
        }

        // 从MinIO删除文件
        try {
            minioClient.removeObject(
                RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(file.getObjectName())
                    .build()
            );
            log.info("从MinIO删除文件成功: bucket={}, object={}", bucketName, file.getObjectName());
        } catch (Exception e) {
            log.warn("从MinIO删除文件失败，文件ID: {}, 错误: {}", fileId, e.getMessage());
        }

        // 从数据库删除记录
        boolean result = removeById(fileId);
        if (result) {
            log.info("文件删除成功，文件ID: {}, 用户ID: {}", fileId, userId);
        }
        return result;
    }

    @Override
    public String getFileUrl(Long fileId) throws Exception {
        UserUploadFile file = getById(fileId);
        if (file == null) {
            throw new BusinessException(ResultCode.DATA_NOT_FOUND, "文件不存在");
        }

        // 生成预签名URL
        try {
            String presignedUrl = minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucketName)
                    .object(file.getObjectName())
                    .expiry(60 * 60) // 1小时
                    .build()
            );
            return presignedUrl;
        } catch (Exception e) {
            log.warn("生成预签名URL失败，文件ID: {}, 错误: {}", fileId, e.getMessage());
            return file.getFileUrl();
        }
    }

    @Override
    public void updateDownloadCount(Long fileId) throws Exception {
        baseMapper.updateDownloadCount(fileId);
    }

    @Override
    public void updateViewCount(Long fileId) throws Exception {
        baseMapper.updateViewCount(fileId);
    }

    @Override
    public void cleanExpiredFiles() throws Exception {
        String expireTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        List<UserUploadFile> expiredFiles = baseMapper.selectExpiredFiles(expireTime);

        for (UserUploadFile file : expiredFiles) {
            try {
                // 从MinIO删除文件
                minioClient.removeObject(
                    RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(file.getObjectName())
                        .build()
                );
                log.info("清理过期文件成功: bucket={}, object={}", bucketName, file.getObjectName());

                // 从数据库删除记录
                removeById(file.getId());
                log.info("清理过期文件成功，文件ID: {}", file.getId());
            } catch (Exception e) {
                log.warn("清理过期文件失败，文件ID: {}, 错误: {}", file.getId(), e.getMessage());
            }
        }
    }

    @Override
    public void validateFileSize(MultipartFile file, Long maxSize) throws Exception {
        if (file.getSize() > maxSize) {
            throw new BusinessException(ResultCode.VALIDATION_FAILED, 
                String.format("文件大小不能超过 %d 字节", maxSize));
        }
    }

    @Override
    public void validateFileType(MultipartFile file, String[] allowedTypes) throws Exception {
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null) {
            throw new BusinessException(ResultCode.VALIDATION_FAILED, "文件名不能为空");
        }

        String fileExtension = getFileExtension(originalFilename).toLowerCase();
        boolean isValid = false;

        for (String allowedType : allowedTypes) {
            if (allowedType.trim().toLowerCase().equals(fileExtension)) {
                isValid = true;
                break;
            }
        }

        if (!isValid) {
            throw new BusinessException(ResultCode.VALIDATION_FAILED, 
                String.format("不支持的文件类型: %s，允许的类型: %s", fileExtension, String.join(", ", allowedTypes)));
        }
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        if (filename == null || filename.lastIndexOf(".") == -1) {
            return "";
        }
        return filename.substring(filename.lastIndexOf(".") + 1);
    }

    /**
     * 生成对象名称
     */
    private String generateObjectName(String uploadType, String fileExtension) {
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String uuid = UUID.randomUUID().toString().replace("-", "");
        return uploadType.toLowerCase() + "/" + timestamp + "_" + uuid + "." + fileExtension;
    }

    /**
     * 判断是否为图片
     */
    private boolean isImage(String contentType) {
        return contentType != null && contentType.startsWith("image/");
    }

    /**
     * 计算SHA256哈希值
     */
    private String calculateSHA256(byte[] data) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(data);
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            log.warn("SHA-256算法不可用，使用MD5替代", e);
            return DigestUtils.md5DigestAsHex(data);
        }
    }


} 